package com.coderman.common.starter.nacos.datajson.service;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.coderman.common.starter.nacos.datajson.config.ConfigFieldBean;
import com.coderman.common.starter.nacos.datajson.config.NacosConfigBean;
import com.coderman.common.starter.nacos.datajson.config.NacosDataJsonProperties;
import com.coderman.common.starter.nacos.datajson.util.SpringContextUtil;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * Description:
 * date: 2020/9/29 7:31 下午
 *
 * @author fanchunshuai
 * @version 1.0.0
 * @since JDK 1.8
 */
@Service
public class DataJsonHelper {

    @Autowired
    private MetaDataService metaDataService;
    private static ExpressionParser expressionParser = new SpelExpressionParser();

    @Autowired
    private NacosDataJsonProperties nacosDataJsonProperties;

    private Logger logger = LoggerFactory.getLogger(DataJsonHelper.class);

    private static ConcurrentHashMap<String,List> configDataMap = new ConcurrentHashMap<>();

    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    /**
     * 配置json初始化
     * @throws Exception
     */
    public synchronized void init() throws Exception{
        ConfigService configService = getConfigService();
        metaDataService.scanNacosJSONEntity();
        List<NacosConfigBean> nacosConfigBeanList = metaDataService.getNacosConfigBeanList();
        Map<String,List> initConfigMap = new HashMap<>();
        //已有数据进行接入
        if(nacosDataJsonProperties.getUsecallback().equals("true")){
            ConfigCallBackService configCallBackService = (ConfigCallBackService)SpringContextUtil.getBean(ConfigCallBackService.class);
            initConfigMap = configCallBackService.getAllConfigList();
        }
        for (NacosConfigBean nacosConfigBean : nacosConfigBeanList){
            regist(nacosConfigBean,initConfigMap,configService);
            addListener(nacosConfigBean.getDataId(),nacosConfigBean.getGroupId(),configService);
        }
    }

    /**
     * 向nacos注册数据
     * @param nacosConfigBean
     * @param configMap
     * @throws NacosException
     */
    private void regist(NacosConfigBean nacosConfigBean, Map<String, List> configMap,ConfigService configService) throws NacosException {
        if(configMap == null || configMap.isEmpty()){
            configMap = new HashMap<>();
        }
        String dataId = nacosConfigBean.getDataId();
        String groupId = nacosConfigBean.getGroupId();
        logger.info("dataId = {},groupId = {}",dataId,groupId);



        String content = configService.getConfig(dataId, groupId, 3000);
        if(StringUtils.isEmpty(content)){
            String metaDataJsonStr = geteMetaDataJson(nacosConfigBean)+"\n";
            StringBuilder builder = new StringBuilder(metaDataJsonStr);
            List configList = configMap.get(nacosConfigBean.getClassSimpleName());
            if(CollectionUtils.isNotEmpty(configList)){
                for (Object obj : configList){
                    builder.append(JSON.toJSONString(obj)+"\n");
                }
            }
            //发布配置到nacos
            boolean b = configService.publishConfig(dataId,groupId,builder.toString());
            logger.info("发布配置到nacos结果:dataId={},groupId={},result={}",dataId,groupId,b);
            return;
        }
        //更新远程数据到本地
        updateConfig(content, nacosConfigBean);
        logger.info("获取nacos数据并存储本地,dataId={},groupId={},",dataId,groupId);

    }


    /**
     * 获取注解元数据-json
     * @param nacosConfigBean
     * @return
     */
    private String geteMetaDataJson(NacosConfigBean nacosConfigBean){
      return JSON.toJSONString(nacosConfigBean);
    }


    /**
     * 针对不同的dataid,groupid添加listener
     *
     * @param dataId
     * @param groupID
     * @throws NacosException
     */
    private void addListener(String dataId, final String groupID,ConfigService configService) throws NacosException {

        configService.addListener(dataId, groupID, new Listener() {
            @Override
            public void receiveConfigInfo(String configInfo) {
                logger.info("收到远程配置更新信息,dataId={},groupId={},准备更新",dataId,groupID);
                exeConvert(configInfo, dataId, groupID);
                logger.info("收到远程配置更新信息,dataId={},groupId={},更新完成",dataId,groupID);
            }

            @Override
            public Executor getExecutor() {
                return null;
            }
        });
    }

    /**
     * 进行远程数据更新
     * @param content
     * @param dataID
     * @param groupID
     */
    private void exeConvert(String content, String dataID, String groupID) {
        List<NacosConfigBean> nacosConfigBeanList = metaDataService.getNacosConfigBeanList();
        NacosConfigBean  nacosConfigBean = nacosConfigBeanList.stream()
                .filter(o->o.getDataId().equals(dataID) && o.getGroupId().equals(groupID))
                .collect(Collectors.toList()).get(0);

        updateConfig(content,nacosConfigBean);
    }

    /**
     * 更新远程配置到本地
     * @param content
     * @param nacosConfigBean
     */
    private void updateConfig(String content, NacosConfigBean nacosConfigBean){
        String[] array = content.split("\n");
        List configList = Lists.newArrayListWithCapacity(array.length);
        for (int i = 1;i < array.length;i ++ ){
            Object object = JSON.parseObject(array[i],nacosConfigBean.getClazz());
            configList.add(object);
        }
        configDataMap.put(nacosConfigBean.getClassSimpleName(),configList);
    }

    /**
     * 通过原生Java方式获取ConfigService
     * @return
     * @throws NacosException
     */
    private ConfigService getConfigService() throws NacosException {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, nacosDiscoveryProperties.getServerAddr());
        return NacosFactory.createConfigService(properties);
    }

    /**
     * 通过entity类名获取对应配置数据列表，获取多条记录
     * @param simpleClassName
     * @return
     */
    public List getByClassName(String simpleClassName){
        return configDataMap.get(simpleClassName);
    }

    /**
     * 通过查询key获取对应的单条记录
     * @param o
     * @return
     */
    public Object getByQuery(Object o){
        logger.info("查询单条记录,queryObj={}",JSON.toJSONString(o));
        if(o == null){
            return null;
        }
        List<NacosConfigBean> nacosConfigBeanList = metaDataService.getNacosConfigBeanList();
        AtomicReference<NacosConfigBean> nacosConfigBeanReference = new AtomicReference<>();
        nacosConfigBeanList.forEach(configBean -> {
            configBean.getClassSimpleName().equals(o.getClass().getSimpleName());
            nacosConfigBeanReference.set(configBean);
        });
        if(nacosConfigBeanReference.get() == null){
            return null;
        }
        NacosConfigBean nacosConfigBean = nacosConfigBeanReference.get();
        List<ConfigFieldBean> configFieldBeanList = nacosConfigBean.getConfigFieldBeanList().stream().filter(configFieldBean -> configFieldBean.isQueryKey()==true).collect(Collectors.toList());

        List list = configDataMap.get(o.getClass().getSimpleName());
        AtomicReference<Object> result = new AtomicReference<>();
        list.forEach(obj->{
            AtomicBoolean b = new AtomicBoolean(true);
            configFieldBeanList.forEach(configFieldBean -> {
                Object value1 = expressionParser.parseExpression(configFieldBean.getFieldName()).getValue(obj);
                Object value2 = expressionParser.parseExpression(configFieldBean.getFieldName()).getValue(o);
                if(!value1.equals(value2)){
                    b.set(false);
                }
            });
            if(b.get()){
                result.set(obj);
            }
        });
        return result.get();
    }
}
